//
//  My4DImage.m
//  V3d_Cocoa
//
//  Created by Y my on 11-3-13.
//  Copyright 2011 NJUST. All rights reserved.
//

#import "My4DImage.h"


@implementation My4DImage

@synthesize p_vmax, p_vmin;
@synthesize colorMap;

-(id) initialize
{
	self = [super initialize];
	if(self)
	{
	data4d_float32 = 0;
	data4d_uint8 = 0;
	data4d_uint16 = 0;
	data4d_virtual = 0;
	p_vmax = 0;
	p_vmin = 0;
	colorMap = [[ColorMap alloc] initWithImageDisplayColorType:colorPseudoMaskColor length:256];
	}
	return self;
}

-(void ****) getData:(ImagePixelType *) type
{
	*type = [self imagePixelType];
	if(*type == V3D_UINT8 || *type == V3D_UINT16 || *type == V3D_FLOAT32)
		return data4d_virtual;
	else 
		return nil;
}

-(void ****) getData
{
	return data4d_virtual;
}

-(BOOL) isEmpty {
	return (!data4d_virtual) ?  YES : NO;
}

-(void) loadImage:(const char *)filename
{
	[self cleanExistData];
	[super loadImage:filename];
	[self setUpData4D];
	NSLog(@"the depth is %d",[self zDim]);
	NSLog(@"the data is %d",[self getData]);
}

-(BOOL) updateMinMaxValues
{
	if([self error] || [self rawData] <= 0 || [self cDim] <= 0 || [self xDim] <= 0 || [self yDim] <= 0 || [self zDim] <= 0)
	{
		NSLog(@"The image data is invalid.\n");
		return NO;
	}
	
	if(p_vmax){free(p_vmax);p_vmax = 0;}
	if(p_vmin){free(p_vmin);p_vmin = 0;}
	
	@try {
		p_vmax = (double *) malloc([self cDim] * sizeof(double));
		p_vmin = (double *) malloc([self cDim] * sizeof(double));
	}
	@catch (...) {
		NSLog(@"Error happened in allocating memory in updateminmaxvalues().\n");
		[self setError:1];
		if(p_vmax){free(p_vmax);p_vmax = 0;}
		if(p_vmin){free(p_vmin);p_vmin = 0;}
		return NO;
	}
	
	V3DLONG i;
	V3DLONG channelPageSize = [self totalUnitNumberPerChannel];
	
	switch ([self imagePixelType]) 
	{
		case V3D_UINT8:
			for(i=0;i<[self cDim];i++)
			{
				unsigned char minvv,maxvv;
				V3DLONG tmppos_min, tmppos_max;
				unsigned char *datahead = (unsigned char *)[self rawDataAtChannel:i];
				minMaxInVector_char(datahead, channelPageSize, &tmppos_min, &minvv, &tmppos_max, &maxvv);
				p_vmax[i] = (double)maxvv; p_vmin[i] = (double)minvv;				
			}
			break;
			
		case V3D_UINT16:
			for(i=0;i<[self cDim];i++)
			{
				USHORTINT16 minvv,maxvv;
				V3DLONG tmppos_min, tmppos_max;
				USHORTINT16 *datahead = (USHORTINT16 *)[self rawDataAtChannel:i];
				minMaxInVector_int16(datahead, channelPageSize, &tmppos_min, &minvv, &tmppos_max, &maxvv);
				p_vmax[i] = (double)maxvv; p_vmin[i] = (double)minvv;
			}
			break;
			
		case V3D_FLOAT32:
			for(i=0;i<[self cDim];i++)
			{
				float minvv,maxvv;
				V3DLONG tmppos_min, tmppos_max;
				float *datahead = (float *)[self rawDataAtChannel:i];
				minMaxInVector_float(datahead, channelPageSize, &tmppos_min, &minvv, &tmppos_max, &maxvv);
				p_vmax[i] = (double)maxvv; p_vmin[i] = (double)minvv;
			}
			break;
			
		default:
			[self setError:1];
			NSLog(@"Invalid data type found in updateminmaxvalues(). Should never happen, - check with V3D developers.");
			return NO;
	}
	
	return YES;
	
}

-(void) createColorMap:(int) len
{
	[self createColorMap:len type:colorPseudoMaskColor];
}

-(void) createColorMap:(int) len type:(ImageDisplayColorType) c
{
	if(colorMap)
	{
		free(colorMap);
		colorMap = nil;
	}
	colorMap = [[ColorMap alloc] initWithImageDisplayColorType:c length:len];
}

-(void) setUpData4D
{
	if([self error] || ![self rawData])
	{
		NSLog(@"Invalid input data for setting up 4D pointers setupData4D().\n");
		return;
	}
	if(![self updateMinMaxValues])
	{
		NSLog(@"Fail to run successfully updateminmaxvalues(). Thus discontinue the setupData4D().\n");
		return;
	}
	switch ( [self imagePixelType] )
	{
		case V3D_UINT8:
			if (![self new4dpointer_v3d_char:&data4d_uint8 withX:[self xDim] andY:[self yDim] andZ:[self zDim] andC:[self cDim] andData1d:[self rawData]])
			{
				[self setError:1];
				return;
			}
			
			data4d_virtual = (void ****)data4d_uint8;
			break;
			
		case V3D_UINT16:
			if (![self new4dpointer_v3d_int16:&data4d_uint16 withX:[self xDim] andY:[self yDim] andZ:[self zDim] andC:[self cDim] andData1d:[self rawData]])
			{
				[self setError:1];
				return;
			}
			data4d_virtual = (void ****)data4d_uint16;
			[self createColorMap:((int)(p_vmax[0])+1000)]; //add 1000 for a safe pool for cell editing.
			printf("set the color map max=%d\n", (int)(p_vmax[0]));
			
			break;
			
		case V3D_FLOAT32:
			if (![self new4dpointer_v3d_float:&data4d_float32 withX:[self xDim] andY:[self yDim] andZ:[self zDim] andC:[self cDim] andData1d:[self rawData]])
			{
				[self setError:1];
				return;
			}
			data4d_virtual = (void ****)data4d_float32;
			break;
			
		default:
			[self setError:1];
			NSLog(@"Invalid data type found in setupData4D(). Should never happen, - check with V3D developers.");
			return;
	}
	
	[self notify];
	///setupDefaultColorChannelMapping();
	
}

-(int) new4dpointer_v3d_char:(unsigned char *****) p withX:(V3DLONG) s0 andY:(V3DLONG) s1 andZ:(V3DLONG) s2 andC:(V3DLONG) s3 andData1d:(unsigned char *) p1d
{
	if(*p){	return 0;}
	
	*p = (unsigned char ****)malloc(sizeof(unsigned char ***)*s3);
	if(!(*p)){return 0;}
	
	for(V3DLONG i = 0; i < s3; i++)
	{
		(*p)[i] = 0;
		if(![self new3dpointer_v3d_char:&((*p)[i]) withX:s0 andY:s1 andZ:s2 andData1d: (p1d+i*s2*s1*s0*sizeof(unsigned char))])
		{
			NSLog(@"Problem happened in creating 3D pointers for channel-%ld.\n", i);
			for(V3DLONG j = 0; j < i; j++)
				free((*p)[j]);
			free(*p);
			*p  = 0;
			return 0;
		}
	}
	return 1;
}
	
-(int) new4dpointer_v3d_int16:(USHORTINT16 *****) p withX:(V3DLONG) s0 andY:(V3DLONG) s1 andZ:(V3DLONG) s2 andC:(V3DLONG) s3 andData1d:(unsigned char *) p1d
{
	if(*p){	return 0;}
	
	*p = (USHORTINT16 ****)malloc(sizeof(USHORTINT16 ***)*s3);
	if(!(*p)){return 0;}
	
	for(V3DLONG i = 0; i < s3; i++)
	{
		(*p)[i] = 0;
		if(![self new3dpointer_v3d_int16:&((*p)[i]) withX:s0 andY:s1 andZ:s2 andData1d: (p1d+i*s2*s1*s0*sizeof(USHORTINT16))])
		{
			NSLog(@"Problem happened in creating 3D pointers for channel-%ld.\n", i);
			for(V3DLONG j = 0; j < i; j++)
				free((*p)[j]);
			free(*p);
			*p  = 0;
			return 0;
		}
	}
	return 1;
}

-(int) new4dpointer_v3d_float:(float *****) p withX:(V3DLONG) s0 andY:(V3DLONG) s1 andZ:(V3DLONG) s2 andC:(V3DLONG) s3 andData1d:(unsigned char *) p1d
{
	if(*p){	return 0;}
	
	*p = (float ****)malloc(sizeof(float ***)*s3);
	if(!(*p)){return 0;}
	
	for(V3DLONG i = 0; i < s3; i++)
	{
		(*p)[i] = 0;
		if(![self new3dpointer_v3d_float:&((*p)[i]) withX:s0 andY:s1 andZ:s2 andData1d: (p1d+i*s2*s1*s0*sizeof(float))])
		{
			NSLog(@"Problem happened in creating 3D pointers for channel-%ld.\n", i);
			for(V3DLONG j = 0; j < i; j++)
				free((*p)[j]);
			free(*p);
			*p  = 0;
			return 0;
		}
	}
	return 1;
}


-(int) new3dpointer_v3d_char:(unsigned char ****) p withX:(V3DLONG) s0 andY:(V3DLONG) s1 andZ:(V3DLONG) s2 andData1d:(unsigned char *) p1d
{
	if(*p){	return 0;}
	
	*p = (unsigned char ***) malloc(sizeof(unsigned char **)*s2);
	if(!(*p)){	return 0;}
	
	for(V3DLONG i = 0; i < s2; i ++)
	{
		(*p)[i] = (unsigned char **) malloc(sizeof(unsigned char *)*s1);
		if(!(*p)[i])
		{
			for(V3DLONG j = 0; j < i; j++)
				free((*p)[i]);
			free(*p);
			*p = 0;
			return 0;
		}
		else
		{
			for(V3DLONG j = 0; j < s1; j++)
				(*p)[i][j] = (unsigned char *) (p1d + i*s0*s1*sizeof(unsigned char) + j*s0*sizeof(unsigned char));
		}
	}
	return 1;
}
	
-(int) new3dpointer_v3d_int16:(USHORTINT16 ****) p withX:(V3DLONG) s0 andY:(V3DLONG) s1 andZ:(V3DLONG) s2 andData1d:(unsigned char *) p1d
{
	if(*p){	return 0;}
	
	*p = (USHORTINT16 ***) malloc(sizeof(USHORTINT16 **)*s2);
	if(!(*p)){	return 0;}
	
	for(V3DLONG i = 0; i < s2; i ++)
	{
		(*p)[i] = (USHORTINT16 **) malloc(sizeof(USHORTINT16 *)*s1);
		if(!(*p)[i])
		{
			for(V3DLONG j = 0; j < i; j++)
				free((*p)[i]);
			free(*p);
			*p = 0;
			return 0;
		}
		else
		{
			for(V3DLONG j = 0; j < s1; j++)
				(*p)[i][j] = (USHORTINT16 *) (p1d + i*s0*s1*sizeof(USHORTINT16) + j*s0*sizeof(USHORTINT16));
		}
	}
	return 1;
}

-(int) new3dpointer_v3d_float:(float ****) p withX:(V3DLONG) s0 andY:(V3DLONG) s1 andZ:(V3DLONG) s2 andData1d:(unsigned char *) p1d
{
	if(*p){	return 0;}
	
	*p = (float ***) malloc(sizeof(float **)*s2);
	if(!(*p)){	return 0;}
	
	for(V3DLONG i = 0; i < s2; i ++)
	{
		(*p)[i] = (float **) malloc(sizeof(float *)*s1);
		if(!(*p)[i])
		{
			for(V3DLONG j = 0; j < i; j++)
				free((*p)[i]);
			free(*p);
			*p = 0;
			return 0;
		}
		else
		{
			for(V3DLONG j = 0; j < s1; j++)
				(*p)[i][j] = (float *) (p1d + i*s0*s1*sizeof(float) + j*s0*sizeof(float));
		}
	}
	return 1;
}


@end
